package repository

import (
	"errors"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEntity"
	"gitee.com/fierce_wolf/go-fox-edge-common/commEnv"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/FileName"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/JarInfoReader"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Map"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Method"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/Slice"
	"gitee.com/fierce_wolf/go-fox-edge-common/commUtil/String"
	"gitee.com/fierce_wolf/go-fox-edge-common/edgeEntity"
	"os"
	"strings"
)

const (
	pack             = "cn.foxtech.device.protocol"
	rootLocationName = "cn.foxtech.device.protocol.RootLocation"
)

type repoLocalJarFileInfoService struct {
}

func (e *repoLocalJarFileInfoService) buildDescription(repoList []map[string]interface{}) map[string]interface{} {
	result := make(map[string]interface{})
	for _, data := range repoList {
		modelName := Map.GetString(data, "modelName", "")
		description := Map.GetString(data, "description", "")

		result[modelName] = description
	}

	return result
}

func (e *repoLocalJarFileInfoService) FindJarInfo(loadJars []string, repoList []map[string]interface{}) ([]map[string]interface{}, error) {
	// 查找目录下所有的jar文件
	jarNameList, err := e.findLocalJarFiles()
	if err != nil {
		return nil, err
	}

	// 分组：按解码器进行分组
	decoderMap, err := e.readJarFiles(jarNameList)
	if err != nil {
		return nil, err
	}

	// 云端仓库的描述信息
	descriptionMap := e.buildDescription(repoList)

	// 读取各文件的时间信息
	createTime := make(map[string]int64)
	updateTime := make(map[string]int64)
	size := make(map[string]int64)
	e.readFileTime(jarNameList, createTime, updateTime, size)

	resultList := make([]map[string]interface{}, 0)

	// 将三级目录：packName/verNum/version：HashMap
	// 以二级列表返回：packName/verNum：HashMap
	for packName, verNumMap := range decoderMap {
		// 第1级：packName
		for verNum, versionMap := range verNumMap.(map[string]interface{}) {
			// 第2级：verNum

			// 以二级列表返回：packName/verNum：HashMap
			result := make(map[string]interface{})
			Map.Append(result, versionMap.(map[string]interface{}))
			result["packName"] = packName
			result["jarVer"] = verNum
			result["description"] = descriptionMap[packName+":"+verNum]

			jarFileName := packName + "." + verNum + ".jar"
			result["fileName"] = jarFileName
			result["createTime"] = createTime[jarFileName]
			result["updateTime"] = updateTime[jarFileName]
			result["size"] = size[jarFileName]
			result["load"] = Slice.Has(loadJars, jarFileName)

			resultList = append(resultList, result)
		}
	}

	return resultList, nil
}

func (e *repoLocalJarFileInfoService) readFileTime(jarNameList []string, createTime, updateTime, size map[string]int64) error {
	for _, fileName := range jarNameList {
		filePath := FileName.GetOsFilePath(commEnv.Service.WorkDir + "/jar/decoder/" + fileName)
		fileInfo, err := os.Stat(filePath)
		if err != nil {
			return err
		}

		fileInfo.ModTime()

		//BasicFileAttributes attributes = FileAttributesUtils.getAttributes("./jar/decoder/" + fileName);
		//createTime[fileName, attributes.creationTime().toMillis());
		//updateTime[fileName, attributes.lastModifiedTime().toMillis());
		//size[fileName, attributes.size());
	}

	return nil
}

func (e *repoLocalJarFileInfoService) findLocalJarFiles() ([]string, error) {
	jarNameList := make([]string, 0)

	fileNameList, err := FileName.List(FileName.GetOsFilePath(commEnv.Service.WorkDir + "/jar/decoder"))
	if err != nil {
		return nil, err
	}

	for _, fileName := range fileNameList {
		if strings.HasSuffix(strings.ToLower(fileName), ".jar") {
			jarNameList = append(jarNameList, fileName)
		}
	}

	return jarNameList, nil
}

func (e *repoLocalJarFileInfoService) findJarNameList() ([]map[string]interface{}, error) {
	mapList := make([]map[string]interface{}, 0)

	jarFileNameList, err := e.findLocalJarFiles()
	if err != nil {
		return nil, err
	}

	for _, jarFileName := range jarFileNameList {
		modelName := LocalJarFileName.GetModelName(jarFileName)
		if Method.HasEmpty(modelName) {
			continue
		}

		data := make(map[string]interface{})
		data["modelName"] = modelName
		data["fileName"] = jarFileName

		mapList = append(mapList, data)
	}

	return mapList, nil
}

func (e *repoLocalJarFileInfoService) readJarFiles(jarNameList []string) (map[string]interface{}, error) {
	result := make(map[string]interface{})

	for _, jarFileName := range jarNameList {
		// 读取jar文件信息
		modelName := LocalJarFileName.GetModelName(jarFileName)
		if Method.HasEmpty(modelName) {
			continue
		}

		jarFileInfo, err := e.ReadJarFileInfo(jarFileName)
		if err != nil {
			return nil, err
		}
		if jarFileInfo == nil {
			continue
		}

		Map.SetValue(result, modelName, jarFileInfo["jarVer"], jarFileInfo)
	}

	return result, nil
}

func (e *repoLocalJarFileInfoService) readJarFile(jarFileName string) (map[string]interface{}, error) {
	// 读取jar文件信息
	modelName := LocalJarFileName.GetModelName(jarFileName)
	if Method.HasEmpty(modelName) {
		return nil, errors.New("文件名不合法")
	}

	jarFileInfo, err := e.ReadJarFileInfo(jarFileName)
	if err != nil {
		return nil, err
	}
	if jarFileInfo == nil {
		return nil, errors.New("读取jar文件信息失败")
	}

	result := make(map[string]interface{})
	Map.Append(result, jarFileInfo)
	result["modelName"] = modelName

	return result, nil
}

func (e *repoLocalJarFileInfoService) ReadJarFileInfo(jarFileName string) (map[string]interface{}, error) {
	filePath := FileName.GetOsFilePath(commEnv.Service.WorkDir + "/jar/decoder/" + jarFileName)
	fileInfo, err := os.Stat(filePath)
	if err != nil {
		return nil, err
	}

	// 文件大小
	fileSize := fileInfo.Size()

	// 从jar文件中，读取jar信息
	jarInfoEntity, err := JarInfoReader.ReadJarInfoEntity(filePath)
	if err != nil {
		return nil, err
	}

	// 获得名空间信息
	jarSpace, err := e.getJarSpace(jarInfoEntity.ClassFileName)
	if err != nil {
		return nil, err
	}

	jarInfo := make(map[string]interface{})
	jarInfo["jarSpace"] = jarSpace
	jarInfo["groupId"] = jarInfoEntity.Properties.GroupId
	jarInfo["artifactId"] = jarInfoEntity.Properties.ArtifactId
	jarInfo["version"] = jarInfoEntity.Properties.Version
	jarInfo["dependencies"] = jarInfoEntity.Dependencies
	jarInfo["classFileName"] = jarInfoEntity.ClassFileName
	jarInfo["fileName"] = jarFileName
	jarInfo["size"] = fileSize

	return jarInfo, nil
}

func (e *repoLocalJarFileInfoService) DeleteFile(fileName string) error {
	filePath := FileName.GetOsFilePath(commEnv.Service.WorkDir + "/jar/decoder/" + fileName)
	return os.Remove(filePath)
}

func (e *repoLocalJarFileInfoService) getJarSpace(classNames []string) (rtn string, err error) {
	// 定义一个函数级的异常拦截，用来防止可能的代码BUG
	defer func() {
		if r := recover(); r != nil {
			rtn = ""
			err = errors.New(String.ToString(r))
		}
	}()

	jarVer, err := e.tryGetJarVer(classNames)
	if err != nil {
		return "", err
	}

	minLength := 0x7fffffff
	list := make([][]string, 0)
	for _, className := range classNames {
		// 检查：是否为RootLocation类
		if className == rootLocationName {
			continue
		}
		if !strings.HasPrefix(className, pack) {
			continue
		}

		subName := className[len(pack)+len(jarVer)+2:]
		items := strings.Split(subName, ".")
		if len(items) < 2 {
			continue
		}

		if minLength > len(items)-1 {
			minLength = len(items) - 1
		}

		list = append(list, items)
	}

	if len(list) == 0 {
		return "", nil
	}

	nameSpace := ""
	for i := 0; i < minLength; i++ {
		// 檢查：是否相同
		same := true
		name := list[0][i]
		for _, items := range list {
			if name != items[i] {
				same = false
				break
			}
		}

		if same {
			nameSpace += "." + name
			continue
		}

		break
	}

	if nameSpace == "" {
		return "", nil
	}

	nameSpace = nameSpace[1:]
	return nameSpace, nil
}

func (e *repoLocalJarFileInfoService) tryGetJarVer(classNames []string) (string, error) {
	for _, className := range classNames {
		// 检查：是否为RootLocation类
		if className == rootLocationName {
			continue
		}

		subName := className[len(pack)+1:]
		items := strings.Split(subName, ".")
		return items[0], nil
	}

	return "", nil
}

func (e *repoLocalJarFileInfoService) ExtendCompJarInfo(compEntityList []commEntity.IEntity) []map[string]interface{} {
	// 取出需要装载的数据
	loadJars, err := LocalJarFileConfig.GetLoads()
	if err != nil {
		return commEntity.BuildMapList(compEntityList)
	}

	mapList := make([]map[string]interface{}, 0)
	for _, entity := range compEntityList {
		compEntity := entity.(*edgeEntity.RepoCompEntity)

		fileName := Map.GetString(compEntity.CompParam, "fileName", "")
		if fileName == "" {
			continue
		}

		// 读取JAR文件中的POM信息
		jarInfo, err := e.ReadJarFileInfo(fileName)
		if err != nil {
			continue
		}
		if jarInfo == nil {
			continue
		}

		data := commEntity.BuildMap(compEntity)
		if data == nil {
			continue
		}

		compParam := Map.GetMap(data, "compParam", make(map[string]interface{}))
		compParam["version"] = jarInfo["version"]
		compParam["size"] = jarInfo["size"]
		compParam["load"] = Slice.Has(loadJars, fileName)

		mapList = append(mapList, data)

	}
	// 根据ID排序
	commEntity.SortMapList(mapList)

	return mapList
}
